// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 包类型声明数据类型和实现
// Go包类型检查的算法。使用
// Config.Check调用包的类型检查器。
// 或者，使用NewChecker创建新的类型检查器
// 并通过调用Checker.Files以增量方式调用它。
// None
// 类型检查包括几个相互依存的阶段：
// None
// 名称解析将程序中的每个标识符（ast.Ident）映射到
// 它所表示的语言对象。
// Use Info.{Defs，Uses，Implicits}用于名称解析的结果。
// None
// 常量折叠计算精确的常量值（常量值）
// 对于作为编译时常量的每个表达式（ast.Expr）。
// 将Info.Types[expr].Value用于常量折叠的结果。
// None
// 类型推断计算每个表达式（ast.Expr）的类型
// 并检查是否符合语言规范。
// 将Info.Types[expr].Type用于类型推断的结果。
// None
// 有关教程，请参见https:
// None
package types

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/constant"
	"go/token"
)

// 错误描述类型检查错误；它实现了错误接口。
// “软”错误是指仍然允许对错误进行有效解释的错误
// 包（如“未使用的变量”）；“硬”错误可能导致不可预测的错误
// 行为如果被忽略。
type Error struct {
	Fset *token.FileSet // 用于解释Pos的文件集
	Pos  token.Pos      // 错误位置
	Msg  string         // 错误消息
	Soft bool           // 如果设置，则错误为“软”

	// go116code是一种未来的API，由于错误代码集很大，因此未进行报告
	// 在实验过程中可能会发生显著变化。工具愿望
	// 要预览此功能，可以使用反射读取GO116代码（请参阅
	// 错误代码（测试。开始），但请注意，不能保证未来的安全
	// 兼容性。
	go116code  errorCode
	go116start token.Pos
	go116end   token.Pos
}

// Error返回一个错误字符串，格式如下：
// 文件名：行：列：消息
func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}

// 导入程序解析到包的导入路径。
// None
// 注意：此接口不支持本地数据的导入
// 出售包裹。请参阅https:
// 如果可能，外部实现应该实现ImporterFrom。
type Importer interface {
	// Import返回给定导入路径的导入包。
	// 语义与ImporterFrom.ImportFrom相似，只是
	// 忽略dir和mode（因为它们不存在）。
	Import(path string) (*Package, error)
}

// ImportMode保留供将来使用。
type ImportMode int

// ImporterFrom解析到包的导入路径；信息技术
// 支持按https进行自动售货：
// 使用go/importer从实现中获取ImporterFrom。
type ImporterFrom interface {
	// 存在导入程序以实现向后兼容性。使命感
	// 导入（路径）与调用ImportFrom（路径“，”0）相同；
	// i、 例如，可能找不到本地出售的软件包。
	// 如果ImporterFrom
	// 他在场。
	Importer

	// ImportFrom返回给定导入的导入包
	// 由位于dir中的包文件导入时的路径。
	// 如果导入失败，除了返回错误外，还返回ImportFrom
	// 鼓励缓存并返回包（如果有）
	// 创建了。这将减少包的不一致性和
	// 由于缺少包而导致后续类型检查器错误。
	// 模式值必须为0；这是留作将来使用的。
	// 必须使用相同的路径和目录调用ImportFrom
	// 退回相同的包裹。
	ImportFrom(path, dir string, mode ImportMode) (*Package, error)
}

// 配置指定类型检查的配置。
// Config的零值是一个随时可用的默认配置。
type Config struct {
	// goVersion描述了公认的Go语言版本。弦
	// 必须遵循格式“go%d.%d”（例如“go1.12”），否则必须
	// 空的空字符串表示最新的语言版本。
	// 如果格式无效，调用类型检查器将导致
	// 惊恐
	goVersion string

	// 如果设置了IgnoreFuncBody，则函数体不会
	// 类型已检查。
	IgnoreFuncBodies bool

	// 如果设置了FakeImportC，`import“C`（对于需要Cgo的包）
	// 声明一个空的“C”包，对于限定的
	// 引用包C（找不到对象）的标识符。
	// 此功能适用于标准库cmd/api工具。
	// None
	// 警告：由于后续错误，影响可能不可预测。
	// 不要随便使用！
	FakeImportC bool

	// 如果设置了go115UsesCgo，则类型检查器希望
	// _cgo_gotypes.go文件是通过运行cmd/cgo to be生成的
	// 作为包源文件提供。限定标识符
	// 关于C包，将由cgo提供
	// _cgo_gotypes.go中的声明。
	// None
	// 同时设置FakeImportC和go115UsesCgo是错误的。
	go115UsesCgo bool

	// 如果出现错误！=nil，则在发现每个错误时调用它
	// 在类型检查期间；err具有动态类型错误。
	// 次要错误（例如，枚举所有类型
	// 包含在无效的递归类型声明中）具有
	// 以“\t”字符开头的错误字符串。
	// 如果Error==nil，则类型检查将在第一个
	// 发现错误。
	Error func(err error)

	// 进口商用于导入从中引用的包
	// 进口报关单。
	// 如果安装的导入器实现ImporterFrom，则类型
	// 检查器调用ImportFrom而不是Import。
	// 如果需要导入程序，类型检查器将报告错误
	// 但没有安装。
	Importer Importer

	// 如果是尺寸！=nil，它为包提供大小调整功能。
	// 否则，使用SizeFor（“gc”、“amd64”）。
	Sizes Sizes

	// 如果设置了DisableUnusedImportCheck，则不会检查包
	// 对于未使用的导入。
	DisableUnusedImportCheck bool
}

func srcimporter_setUsesCgo(conf *Config) {
	conf.go115UsesCgo = true
}

// Info结构可在api_notypeparams.go和api_typeparams.go中找到。

// TypeOf返回表达式e的类型，如果找不到，则返回nil。
// 前提条件：填充类型、用途和定义贴图。
// None
func (info *Info) TypeOf(e ast.Expr) Type {
	if t, ok := info.Types[e]; ok {
		return t.Type
	}
	if id, _ := e.(*ast.Ident); id != nil {
		if obj := info.ObjectOf(id); obj != nil {
			return obj.Type()
		}
	}
	return nil
}

// ObjectOf返回由指定id表示的对象，
// 如果找不到，则为零。
// None
// 如果id是嵌入式结构字段，ObjectOf将返回字段（*Var）
// 它定义的是它所使用的类型（*TypeName）。
// None
// 前提条件：已填充Uses和Defs贴图。
// None
func (info *Info) ObjectOf(id *ast.Ident) Object {
	if obj := info.Defs[id]; obj != nil {
		return obj
	}
	return info.Uses[id]
}

// TypeAndValue报告类型和值（对于常量）
// 对应表达式的。
type TypeAndValue struct {
	mode  operandMode
	Type  Type
	Value constant.Value
}

// IsVoid报告相应的表达式
// 是一个没有结果的函数调用。
func (tv TypeAndValue) IsVoid() bool {
	return tv.mode == novalue
}

// IsType报告相应的表达式是否指定类型。
func (tv TypeAndValue) IsType() bool {
	return tv.mode == typexpr
}

// IsBuiltin报告相应的表达式是否表示
// 一个（可能用括号括起来的）内置函数。
func (tv TypeAndValue) IsBuiltin() bool {
	return tv.mode == builtin
}

// IsValue报告相应的表达式是否为值。
// 内置项不被视为值。常量值具有非唯一性-
// 零值。
func (tv TypeAndValue) IsValue() bool {
	switch tv.mode {
	case constant_, variable, mapindex, value, commaok, commaerr:
		return true
	}
	return false
}

// IsNil报告相应的表达式是否表示
// 预先声明的值为零。
func (tv TypeAndValue) IsNil() bool {
	return tv.mode == value && tv.Type == Typ[UntypedNil]
}

// Addressable报告是否使用相应的表达式
// 可寻址（https:
func (tv TypeAndValue) Addressable() bool {
	return tv.mode == variable
}

// Assignable报告是否包含相应的表达式
// 可分配给（提供正确类型的值）。
func (tv TypeAndValue) Assignable() bool {
	return tv.mode == variable || tv.mode == mapindex
}

// HasOk报告是否可以使用相应的表达式
// 用于逗号ok赋值的rhs。
func (tv TypeAndValue) HasOk() bool {
	return tv.mode == commaok || tv.mode == mapindex
}

// _推断报告_推断类型参数和签名
// 用于使用类型推断的参数化函数调用。
type _Inferred struct {
	Targs []Type
	Sig   *Signature
}

// 初始值设定项描述一个包级别变量，或者在case中描述一个变量列表
// 多值初始化表达式，以及相应的初始化
// 表示
type Initializer struct {
	Lhs []*Var // var Lhs=Rhs
	Rhs ast.Expr
}

func (init *Initializer) String() string {
	var buf bytes.Buffer
	for i, lhs := range init.Lhs {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.WriteString(lhs.Name())
	}
	buf.WriteString(" = ")
	WriteExpr(&buf, init.Rhs)
	return buf.String()
}

// 检查类型检查包并返回结果包对象和
// 第一个错误（如果有）。此外，如果信息！=无，检查填充每个
// Info结构中的非nil映射的。
// None
// 如果未发生错误，则将包标记为已完成，否则将被删除
// 不完整。请参阅Config.Error以了解在存在
// 错误。
// None
// 包由*ast.Files和相应文件的列表指定
// 文件集，以及用以标识包的包路径。
// 清洁路径不得为空或点（“.”）。
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}

// AssertableTo报告类型V的值是否可以断言为类型T。
func AssertableTo(V *Interface, T Type) bool {
	m, _ := (*Checker)(nil).assertableTo(V, T)
	return m == nil
}

// AssignableTo报告V类型的值是否可分配给T类型的变量。
func AssignableTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	ok, _ := x.assignableTo(nil, T, nil) // 非常数x不需要检查
	return ok
}

// ConvertibleTo报告类型V的值是否可转换为类型T的值。
func ConvertibleTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.convertibleTo(nil, T, nil) // 非常数x不需要检查
}

// Implements报告类型V是否实现接口T。
func Implements(V Type, T *Interface) bool {
	f, _ := MissingMethod(V, T, true)
	return f == nil
}

// 相同报告x和y是否为相同类型。
// 签名类型的接收者将被忽略。
func Identical(x, y Type) bool {
	return (*Checker)(nil).identical(x, y)
}

// IdenticalIgnoreTags报告如果忽略标记，x和y是否为相同的类型。
// 签名类型的接收者将被忽略。
func IdenticalIgnoreTags(x, y Type) bool {
	return (*Checker)(nil).identicalIgnoreTags(x, y)
}
